package com.zm.admin.zmeng.controller;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.poi.excel.ExcelUtil;
import cn.hutool.poi.excel.ExcelWriter;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.api.R;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.zm.admin.annotation.SysLogTag;
import com.zm.admin.quartz.QuartzService;
import com.zm.admin.utils.ResultBean;
import com.zm.admin.zmeng.domain.*;
import com.zm.admin.zmeng.dto.DictDto;
import com.zm.admin.zmeng.dto.ScheduleJobDto;
import com.zm.admin.zmeng.dto.ScheduleLogDto;
import com.zm.admin.zmeng.mapper.ZmScheduleCronMapper;
import com.zm.admin.zmeng.mapper.ZmScheduleJobMapper;
import com.zm.admin.zmeng.mapper.ZmScheduleLogMapper;
import com.zm.admin.zmeng.service.ZmScheduleCronService;
import com.zm.admin.zmeng.service.ZmScheduleJobService;
import com.zm.admin.zmeng.service.ZmScheduleLogService;
import lombok.extern.slf4j.Slf4j;
import org.quartz.Scheduler;
import org.springframework.http.converter.HttpMessageNotWritableException;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Random;
import java.util.stream.Collectors;

/***
 *
 * @Auther: MuYun
 * @Date: 2023/1/25 21:02
 * @Description： 定时任务相关接口
 *
 */
@RequestMapping("/schedule")
@RestController
@Slf4j
public class ScheduleController {

    @Resource
    private ZmScheduleJobService zmScheduleJobService;

    @Resource
    private ZmScheduleJobMapper zmScheduleJobMapper;

    @Resource
    private ZmScheduleCronMapper zmScheduleCronMapper;

    @Resource
    private ZmScheduleCronService zmScheduleCronService;

    @Resource
    private ZmScheduleLogService zmScheduleLogService;

    @Resource
    private ZmScheduleLogMapper zmScheduleLogMapper;


    @Resource
    private Scheduler scheduler;

    /**
     * 获取定时任务列表
     * @param jobDto
     * @return
     */
    @RequestMapping("/jobList")
    public ResultBean jobList(ScheduleJobDto jobDto){
        ResultBean resultBean=new ResultBean();
        Integer pageNo=1;
        Integer pageSize=10;
        try{
            QueryWrapper<ZmScheduleJob> wrapper = designQueryMapper(jobDto);
            if(ObjectUtil.isNotEmpty(jobDto.getPageNo())){
                pageNo=jobDto.getPageNo();
            }
            if(ObjectUtil.isNotEmpty(jobDto.getPageSize())){
                pageSize= jobDto.getPageSize();
            }
            Page<ZmScheduleJob> page = zmScheduleJobService.page(new Page<>(pageNo, pageSize), wrapper);
            resultBean.setCode(ResultBean.CODE_SUCCESS).setData(page);
        }catch (Exception e){
            log.error("获取定时任务列表接口报错:{}",e);
            resultBean.setCode(ResultBean.CODE_ERROR).setMsg(e.getMessage());
        }
        return resultBean;
    }

    /**
     * 获取定时任务表达式列表
     * @return
     */
    @RequestMapping("/cronList")
    public ResultBean cronList(ScheduleJobDto jobDto){
        ResultBean resultBean=new ResultBean();
        Integer pageNo=1;
        Integer pageSize=5;
        try{
            if(ObjectUtil.isNotEmpty(jobDto.getPageNo())){
                pageNo=jobDto.getPageNo();
            }
            if(ObjectUtil.isNotEmpty(jobDto.getPageSize())){
                pageSize= jobDto.getPageSize();
            }
            Page<ZmScheduleCron> page = zmScheduleCronService.page(new Page<>(pageNo, pageSize));
            resultBean.setCode(ResultBean.CODE_SUCCESS).setData(page);
        }catch (Exception e){
            log.error("获取定时策略接口出错：{}",e);
        }
        return resultBean;
    }

    /**
     * 获取定时任务日志列表呢
     * @param logDto
     * @return
     */
    @RequestMapping("/logList")
    public ResultBean logList(ScheduleLogDto logDto){
        ResultBean resultBean=new ResultBean();
        Integer pageNo=1;
        Integer pageSize=10;
        try{
            if(ObjectUtil.isNotEmpty(logDto.getPageNo())){
                pageNo=logDto.getPageNo();
            }
            if(ObjectUtil.isNotEmpty(logDto.getPageSize())){
                pageSize= logDto.getPageSize();
            }
            QueryWrapper<ZmScheduleLog> wrapper=designLogQueryWrapper(logDto);
            Page<ZmScheduleLog> page = zmScheduleLogService.page(new Page<>(pageNo, pageSize),wrapper);
            resultBean.setCode(ResultBean.CODE_SUCCESS).setData(page);
        }catch (Exception e){
            log.error("获取定时策略接口出错：{}",e);
        }
        return resultBean;
    }

    /**
     * 定时任务excel导出
     * @param response
     */
    @RequestMapping("/jobExport")
    @ResponseBody
    @SysLogTag(desc = "定时任务excel导出")
    public ResultBean jobExport(HttpServletResponse response){
        ResultBean resultBean=new ResultBean();
        QueryWrapper<ZmScheduleJob> wrapper=new QueryWrapper<>();
        ExcelWriter writer =ExcelUtil.getWriter();
        try {
            wrapper.lambda().ge(ZmScheduleJob::getStatus, "0");
            //不查id,密码，盐值，头像（因为是路径地址
            wrapper.select(ZmScheduleJob.class,i->!"id".equals(i.getColumn()));
            List<ZmScheduleJob> zmScheduleJobList = zmScheduleJobMapper.selectList(wrapper);
            if(zmScheduleJobList.size()!=0){
                //组装文件名
                SimpleDateFormat format=new SimpleDateFormat("yyyyMMdd");
                String time=format.format(new Date());
                String newFilename=time+new Random().nextInt(10000)+ ".xls";
                // 通过工具类创建writer，默认创建xls格式
                writer = ExcelUtil.getWriter();
                writer.addHeaderAlias("id", "编号");
                writer.addHeaderAlias("jobName", "任务名");
                writer.addHeaderAlias("beanName", "bean名称");
                writer.addHeaderAlias("params", "参数");
                writer.addHeaderAlias("status", "状态");
                writer.addHeaderAlias("cron", "定时策略");
                writer.addHeaderAlias("remark", "备注");
                // 合并7个单元格后的标题行，使用默认标题样式
                writer.merge(6, "定时任务信息");
                int index=0;
                Iterator<ZmScheduleJob> iterator = zmScheduleJobList.iterator();
                while(iterator.hasNext()){
                    ZmScheduleJob scheduleJob=iterator.next();
                    int i=index+1;
                    //组装编号
                    scheduleJob.setId(Integer.toString(i));
                    index++;
                    //状态改造
                    if("1".equals(scheduleJob.getStatus())){
                        scheduleJob.setStatus("启用");
                    }else {
                        scheduleJob.setStatus("停用");
                    }
                    SimpleDateFormat format1=new SimpleDateFormat("yyyy-MM-dd HH:mm");
                }
                writer.setOnlyAlias(true);
                // 写到excel，使用默认样式，强制输出标题
                writer.write(zmScheduleJobList, true);
                //response为HttpServletResponse对象
                response.setContentType("application/vnd.ms-excel;charset=utf-8");
                ServletOutputStream out=response.getOutputStream();
                //out为OutputStream，需要写出到的目标流
                writer.flush(out);
                resultBean.setCode(ResultBean.CODE_SUCCESS);
            }
        }catch (Exception e){
            log.info("导出定时任务日志excel接口异常：{}",e);
            resultBean.setCode(ResultBean.CODE_ERROR).setMsg(e.getMessage());
        }
        finally {
            // 关闭writer，释放内存
            writer.close();
        }
        return resultBean;
    }

    /**
     * 定时任务日志excel导出
     * @param response
     */
    @RequestMapping("/logExport")
    @ResponseBody
    @SysLogTag(desc = "定时任务日志excel导出")
    public ResultBean export(@RequestParam("id")String id, HttpServletResponse response){
        ResultBean resultBean=new ResultBean();
        QueryWrapper<ZmScheduleLog> wrapper=new QueryWrapper<>();
        ExcelWriter writer =ExcelUtil.getWriter();
        try {
            wrapper.lambda().ge(ZmScheduleLog::getStatus, "0");
            wrapper.lambda().eq(ZmScheduleLog::getJobId,id);
            //不查id,密码，盐值，头像（因为是路径地址
            wrapper.select(ZmScheduleLog.class,i->!"id".equals(i.getColumn()));
            wrapper.select(ZmScheduleLog.class,i->!"jobId".equals(i.getColumn()));
            List<ZmScheduleLog> zmScheduleLogList = zmScheduleLogMapper.selectList(wrapper);
            if(zmScheduleLogList.size()!=0){
                //组装文件名
                SimpleDateFormat format=new SimpleDateFormat("yyyyMMdd");
                String time=format.format(new Date());
                String newFilename=time+new Random().nextInt(10000)+ ".xls";
                // 通过工具类创建writer，默认创建xls格式
                writer = ExcelUtil.getWriter();
                writer.addHeaderAlias("id", "编号");
                writer.addHeaderAlias("beanName", "bean名称");
                writer.addHeaderAlias("params", "参数");
                writer.addHeaderAlias("status", "状态");
                writer.addHeaderAlias("exception", "异常信息");
                writer.addHeaderAlias("times", "执行耗时");
                //借用，不然重新赋值进去还是Date格式，excel乱码
                writer.addHeaderAlias("jobId", "日志创建时间");
                // 合并7个单元格后的标题行，使用默认标题样式
                writer.merge(6, "定时任务日志信息");
                int index=0;
                Iterator<ZmScheduleLog> iterator = zmScheduleLogList.iterator();
                while(iterator.hasNext()){
                    ZmScheduleLog scheduleLog=iterator.next();
                    int i=index+1;
                    //组装编号
                    scheduleLog.setId(Integer.toString(i));
                    index++;
                    //状态改造
                    if("1".equals(scheduleLog.getStatus())){
                        scheduleLog.setStatus("正常");
                        scheduleLog.setException("未出现异常");
                    }else {
                        scheduleLog.setStatus("异常");
                    }
                    SimpleDateFormat format1=new SimpleDateFormat("yyyy-MM-dd HH:mm");
                    scheduleLog.setJobId(format1.format(scheduleLog.getCreateTime()));
                }
                writer.setOnlyAlias(true);
                // 写到excel，使用默认样式，强制输出标题
                writer.write(zmScheduleLogList, true);
                //response为HttpServletResponse对象
                response.setContentType("application/vnd.ms-excel;charset=utf-8");
                ServletOutputStream out=response.getOutputStream();
                //out为OutputStream，需要写出到的目标流
                writer.flush(out);
                resultBean.setCode(ResultBean.CODE_SUCCESS);
            }
        }catch (Exception e){
            log.info("导出定时任务日志excel接口异常：{}",e);
            resultBean.setCode(ResultBean.CODE_ERROR).setMsg(e.getMessage());
        }
        finally {
            // 关闭writer，释放内存
            writer.close();
        }
        return resultBean;
    }

    /**
     * 新增定时任务
     * @param scheduleJob
     * @return
     */
    @RequestMapping("/add")
    @SysLogTag(desc = "定时任务新增")
    public ResultBean add(ZmScheduleJob scheduleJob){
        ResultBean resultBean=new ResultBean();
        try{
            scheduleJob.setCreateTime(DateUtil.date());
            zmScheduleJobMapper.insert(scheduleJob);
            //同步新增的定时任务
            QuartzService.createJob(scheduler,scheduleJob);
            resultBean.setCode(ResultBean.CODE_SUCCESS).setMsg("定时任务新增成功");
        }catch (Exception e){
            log.error("新增定时任务报错:{}",e);
            resultBean.setCode(ResultBean.CODE_ERROR).setMsg(e.getMessage());
        }
        return resultBean;
    }
    /**
     * 定时任务修改
     * @param scheduleJob
     * @return
     */
    @RequestMapping("/update")
    @SysLogTag(desc = "定时任务修改")
    public ResultBean update(ZmScheduleJob scheduleJob){
        ResultBean resultBean=new ResultBean();
        try{
            zmScheduleJobMapper.updateById(scheduleJob);
            QuartzService.updateJob(scheduler,scheduleJob);
            resultBean.setCode(ResultBean.CODE_SUCCESS);
        }catch (Exception e){
            log.error("定时任务修改接口报错:{}",e);
            resultBean.setCode(ResultBean.CODE_ERROR).setMsg(e.getMessage());
        }
        return resultBean;
    }

    /**
     * 定时任务删除
     * @param list
     * @return
     */
    @RequestMapping("/del")
    @SysLogTag(desc = "定时任务删除")
    public ResultBean del(@RequestBody List<ZmScheduleJob> list){
        ResultBean resultBean=new ResultBean();
        try{
            for(ZmScheduleJob job:list){
                job.setStatus("-1");
            }
            zmScheduleJobService.updateBatchById(list);
            //同步删除的定时任务
            for(ZmScheduleJob job:list){
                QuartzService.deleteJob(scheduler,Long.parseLong(job.getId()));
            }
            resultBean.setCode(ResultBean.CODE_SUCCESS).setMsg("删除定时任务记录成功");
        }catch (Exception e){
            log.error("删除字典记录接口报错",e);
            resultBean.setCode(ResultBean.CODE_ERROR).setMsg("删除定时任务接口报错");
        }
        return resultBean;
    }

    private static QueryWrapper<ZmScheduleJob> designQueryMapper(ScheduleJobDto jobDto){
        QueryWrapper<ZmScheduleJob> wrapper=new QueryWrapper<>();
        if(StrUtil.isNotEmpty(jobDto.getJobName())){
            wrapper.lambda().like(ZmScheduleJob::getJobName,jobDto.getJobName());
        }
        if(StrUtil.isNotEmpty(jobDto.getParams())){
            wrapper.lambda().like(ZmScheduleJob::getParams,jobDto.getParams());
        }
        if(StrUtil.isNotEmpty(jobDto.getStatus())){
            wrapper.lambda().eq(ZmScheduleJob::getStatus,jobDto.getStatus());
        }
        wrapper.lambda().ne(ZmScheduleJob::getStatus,"-1");
        return wrapper;
    }

    private static QueryWrapper<ZmScheduleLog> designLogQueryWrapper(ScheduleLogDto logDto){
        QueryWrapper<ZmScheduleLog> wrapper=new QueryWrapper<>();
        if(StrUtil.isNotEmpty(logDto.getStatus())){
            String status="正常".equals(logDto.getStatus())?"1":"0";
            logDto.setStatus(status);
            wrapper.lambda().eq(ZmScheduleLog::getStatus,logDto.getStatus());
        }
        if(StrUtil.isNotEmpty(logDto.getStartTime())){
            wrapper.lambda().ge(ZmScheduleLog::getCreateTime,logDto.getStartTime());
        }
        if(StrUtil.isNotEmpty(logDto.getEndTime())){
            wrapper.lambda().le(ZmScheduleLog::getCreateTime,logDto.getEndTime());
        }
        wrapper.lambda().eq(ZmScheduleLog::getJobId,logDto.getJobId());
        return wrapper;
    }
}
